use crate::capi::destroy_signal_msg;
use crate::entity::{ControlSessionStatus, RequestCmd, SessionNotifyCmd};
use crate::ffi::{
    buffer_t, ice_info_t, session_create_by_remote_t, session_error_info_t,
    session_ice_cands_update_t, session_id_t, session_sdp_info_t, session_status_info_t,
    signal_client_opt_t, signal_error_e_signal_error_disconnect, signal_error_e_signal_error_fail,
    signal_error_e_signal_error_invalid_param, signal_msg_id_t_smi_remote_ice_candidate_update,
    signal_msg_id_t_smi_remote_sdp_arrived, signal_msg_id_t_smi_session_closing,
    signal_msg_id_t_smi_session_created, signal_msg_id_t_smi_session_created_by_remote,
    signal_msg_id_t_smi_session_error, signal_msg_id_t_smi_session_status, signal_msg_t,
};
use crate::netdb::ffi::{gethostbyname, in_addr, AF_INET};
use crate::url::Url;
use crate::util::copy_str_to_slice;
use crate::Result;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use cjson::Json;
use core::ffi::c_void;
use core::ptr::null_mut;
use embed_std::ffi::{CStr, CString};
use embed_std::time::now;
use embed_std::{cstr_ptr, debugln, errorln, infoln};
use embedded_websocket::framer::FramerError;
use embedded_websocket::tcp::{IoError, TcpStream};
use embedded_websocket::{
    framer::{Framer, ReadResult},
    Client, EmptyRng, WebSocketClient, WebSocketCloseStatusCode, WebSocketOptions,
    WebSocketSendMessageType, WebSocketState,
};

const BUF_SIZE: usize = 1024 * 16;
const DOMAIN: &str = "user";
const MAX_SESSION: usize = 16;
const INVALID_SESSION_ID: usize = 0;
const HEARTBEAT_INTERVAL: u64 = 60;

macro_rules! json_get_str {
    ($node_name:ident, $node:expr, $field_name:expr) => {
        let $node_name = $node.get(cstr_ptr!($field_name)).ok_or_else(|| {
            errorln!("{} field lost", $field_name);
            signal_error_e_signal_error_fail
        })?;
        let $node_name = $node_name.as_str().ok_or_else(|| {
            errorln!("{} field invalid", $field_name);
            signal_error_e_signal_error_fail
        })?;
    };
}

macro_rules! json_get_int {
    ($node_name:ident, $node:expr, $field_name:expr) => {
        let $node_name = $node.get(cstr_ptr!($field_name)).ok_or_else(|| {
            errorln!("{} field lost", $field_name);
            signal_error_e_signal_error_fail
        })?;
        let $node_name = $node_name.as_i64().ok_or_else(|| {
            errorln!("{} field invalid", $field_name);
            signal_error_e_signal_error_fail
        })?;
    };
}

#[derive(Clone)]
pub struct Session {
    pub id: String,
    //secs
    pub ctime: u64,
    //secs
    pub utime: u64,
}

type WSC = WebSocketClient<EmptyRng>;
pub struct WsClient {
    opt: signal_client_opt_t,
    stream: Option<TcpStream>,
    client: WebSocketClient<EmptyRng>,
    read_buf: [u8; BUF_SIZE],
    read_cursor: usize,
    write_buf: [u8; BUF_SIZE],
    frame_buf: [u8; BUF_SIZE],
    sessions: Vec<Option<Session>>,
    last_hb_time: u64,
    framer: *mut c_void,
}

impl WsClient {
    pub fn new(opt: signal_client_opt_t) -> Self {
        let mut sessions = Vec::new();
        //slot 0 is reserved for invalid session id
        for _ in 0..MAX_SESSION + 1 {
            sessions.push(None);
        }
        Self {
            opt,
            stream: None,
            client: WebSocketClient::new_client(EmptyRng::new()),
            read_buf: [0u8; BUF_SIZE],
            read_cursor: 0,
            write_buf: [0u8; BUF_SIZE],
            frame_buf: [0u8; BUF_SIZE],
            sessions,
            last_hb_time: 0,
            framer: null_mut(),
        }
    }

    pub fn login(&mut self) -> Result<()> {
        self.connect()?;
        Ok(())
    }

    pub fn is_connected(&self) -> bool {
        if let Some(ref stream) = self.stream {
            if self.client.state == WebSocketState::Open {
                return stream.is_connected();
            }
        }
        false
    }

    fn connect(&mut self) -> Result<()> {
        let s = unsafe { CStr::from_ptr(self.opt.url.as_ptr()) }
            .to_str()
            .map_err(|_e| {
                errorln!("invalid signal url");
                signal_error_e_signal_error_invalid_param
            })?;
        infoln!("url {}", s);
        let url = Self::parse_url(s)?;
        let ipv4 = Self::parse_ipv4(url.host)?;
        infoln!("connect to {:x}:{}", ipv4, url.port.unwrap());
        self.stream = Some(TcpStream::connect(ipv4, url.port.unwrap()).map_err(|e| {
            errorln!(
                "connect to {}:{} failed: {:?}",
                url.host,
                url.port.unwrap(),
                e
            );
            signal_error_e_signal_error_invalid_param
        })?);
        // initiate a websocket opening handshake
        let host = format!("{}:{}", url.host, url.port.unwrap());
        let origin = format!("{}://{}:{}", url.schema, url.host, url.port.unwrap());
        let user = unsafe { CStr::from_ptr(self.opt.user.as_ptr()) }
            .to_str()
            .unwrap();
        let (term_id, term_domain) = if let Some(pos) = user.find("@") {
            (&user[..pos], &user[pos + 1..])
        } else {
            (user, DOMAIN)
        };
        let pwd = unsafe { CStr::from_ptr(self.opt.pwd.as_ptr()) }
            .to_str()
            .unwrap();
        let path = format!(
            "{}?term_domain={}&term_id={}&pwd={}",
            url.uri, term_domain, term_id, pwd
        );
        let websocket_options = WebSocketOptions {
            path: path.as_str(),
            host: host.as_str(),
            origin: origin.as_str(),
            sub_protocols: None,
            additional_headers: None,
        };

        //for reconnect, clear state
        self.client = WebSocketClient::new_client(EmptyRng::new());
        if !self.framer.is_null() {
            let f = unsafe{ Box::from_raw(self.framer as *mut Framer<EmptyRng, Client>) };
            drop(f);
            self.framer = null_mut();
        }
        let f = Box::new(Framer::new(
            &mut self.read_buf,
            &mut self.read_cursor,
            &mut self.write_buf,
            &mut self.client,
        ));
        self.framer = Box::into_raw(f) as *mut c_void;
        let framer =
            unsafe { Box::leak(Box::from_raw(self.framer as *mut Framer<EmptyRng, Client>)) };
        framer
            .connect(self.stream.as_mut().unwrap(), &websocket_options)
            .map_err(|e| {
                errorln!("framer connect failed:{:?}", e);
                signal_error_e_signal_error_fail
            })?;
        Ok(())
    }

    fn send(&mut self, txt: &str) -> Result<()> {
        if self.framer.is_null() {
            errorln!("Bug: framer not inited");
            return Err(signal_error_e_signal_error_invalid_param);
        }
        let framer =
            unsafe { Box::leak(Box::from_raw(self.framer as *mut Framer<EmptyRng, Client>)) };
        if let Some(ref mut stream) = self.stream {
            framer
                .write(stream, WebSocketSendMessageType::Text, true, txt.as_bytes())
                .map_err(|e| {
                    errorln!("send {} failed: {:?}", txt, e);
                    signal_error_e_signal_error_fail
                })?;
            self.last_hb_time = now().as_secs();
        } else {
            errorln!("tcp stream not available");
        }
        Ok(())
    }

    fn send_err_report(&mut self, session_id: &str, rc: i32, rd: String) {
        let body = format!(
            r#"{{ "cmd": {}, "data": {{ "session_id": "{}", "status": {}, "error": {{ "rc": {}, "rd": {} }} }} }}"#,
            u8::from(RequestCmd::ControlSession),
            session_id,
            u8::from(ControlSessionStatus::HasError),
            rc,
            rd,
        );
        debugln!("send error report to session {}", session_id);
        let _ = self.send(body.as_str()).map_err(|e| {
            errorln!("send error report failed: {}", e);
        });
    }

    fn parse_url(s: &str) -> Result<Url> {
        let mut url = Url::parse(s).map_err(|e| {
            errorln!("url invalid:{}", e);
            signal_error_e_signal_error_invalid_param
        })?;
        if url.port.is_none() {
            url.port = Some(match url.schema {
                "ws" | "http" => 80,
                "wss" | "https" => 443,
                other => {
                    errorln!("unknown schema {}", other);
                    return Err(signal_error_e_signal_error_invalid_param);
                }
            });
        }
        Ok(url)
    }

    fn parse_ipv4(s: &str) -> Result<u32> {
        unsafe {
            let host = CString::from_vec_unchecked(Vec::from(s.as_bytes()));
            loop {
                //@TODO 存在多线程风险
                let h = gethostbyname(host.as_ptr());
                if h.is_null() {
                    break;
                }
                let host = &*h;
                if host.h_addrtype == AF_INET as i32 {
                    debugln!(
                        "hostname {}",
                        CStr::from_ptr(host.h_name).to_str().unwrap_or("unknown")
                    );
                    let mut ipv4 = 0;
                    const MAX_SIZE: usize = 16;
                    for i in 0..MAX_SIZE {
                        let p = *host.h_addr_list.offset(i as isize);
                        if p.is_null() {
                            break;
                        }
                        ipv4 = (*(p as *mut in_addr)).s_addr;
                    }
                    return Ok(ipv4.to_be());
                }
            }
        }
        errorln!("dns parse {} failed", s);
        Err(signal_error_e_signal_error_invalid_param)
    }

    pub fn create_session(&mut self, peer_id: &CStr) -> Result<()> {
        let body = format!(
            r#"{{ "cmd": {}, "data": {{ "peer_id": "{}" }} }}"#,
            u8::from(RequestCmd::CreateSession),
            peer_id.to_str().map_err(|e| {
                errorln!("peer_id to str failed: {:?}", e);
                signal_error_e_signal_error_invalid_param
            })?
        );
        debugln!(
            "create session to peer_id {}: {}",
            peer_id.to_str().unwrap_or("invalid peer_id"),
            body
        );
        self.send(body.as_str())
    }

    pub fn destroy_session(&mut self, session_id: session_id_t) {
        let ses = match self.get_session_id(session_id) {
            Some(ses) => ses,
            None => {
                errorln!("session {} not exist", session_id);
                return;
            }
        };
        let body = format!(
            r#"{{ "cmd": {}, "data": {{ "session_id": "{}", "status": {} }} }}"#,
            u8::from(RequestCmd::ControlSession),
            ses.id,
            u8::from(ControlSessionStatus::RequestToClose)
        );
        debugln!("destroy session {}", session_id);
        let _ = self.send(body.as_str()).map_err(|e| {
            errorln!("send destroy_session cmd failed: {}", e);
        });
    }

    fn close(&mut self) {
        if self.framer.is_null() {
            errorln!("Bug: framer not inited");
            return;
        }
        let framer =
            unsafe { Box::leak(Box::from_raw(self.framer as *mut Framer<EmptyRng, Client>)) };

        debugln!("send close message");
        if let Some(ref mut stream) = self.stream {
            let _ = framer
                .close(stream, WebSocketCloseStatusCode::NormalClosure, None)
                .map_err(|e| {
                    errorln!("close failed: {:?}", e);
                });
        } else {
            errorln!("tcp stream not available");
        }
    }

    pub fn update_local_sdp(&mut self, session_id: session_id_t, sdp: &CStr) -> Result<()> {
        let ses = self.get_session_id(session_id).ok_or_else(|| {
            format!("session {} not exist", session_id);
            signal_error_e_signal_error_fail
        })?;
        let s = sdp.to_str().map_err(|e| {
            errorln!("sdp to str failed: {}", e);
            signal_error_e_signal_error_fail
        })?;
        debugln!("try to update local sdp {}", s);
        let buff_len = s.len() * 2;
        let mut tmp = Vec::with_capacity(buff_len);
        unsafe { tmp.set_len(buff_len) };
        let v = binascii::b64encode(s.as_bytes(), tmp.as_mut_slice()).map_err(|e| {
            errorln!(
                "base64 sdp failed: {:?}, sdp len {} base64 buffer len {}",
                e,
                s.len(),
                buff_len
            );
            signal_error_e_signal_error_invalid_param
        })?;
        let body = format!(
            r#"{{ "cmd":{},"data":{{"session_id":"{}","sdp":"{}"}}}}"#,
            u8::from(RequestCmd::UpdateSdp),
            ses.id,
            unsafe { alloc::str::from_utf8_unchecked(v) }
        );
        debugln!(
            "session {} update local sdp: {}",
            session_id,
            sdp.to_str().unwrap_or("invalid sdp")
        );
        self.send(body.as_str())
    }

    pub fn update_ice_candidate(&mut self, session_id: session_id_t, sdp: &CStr) -> Result<()> {
        let ses = self.get_session_id(session_id).ok_or_else(|| {
            format!("session {} not exist", session_id);
            signal_error_e_signal_error_fail
        })?;
        let s = sdp.to_str().map_err(|e| {
            errorln!("ice cands to str failed: {}", e);
            signal_error_e_signal_error_fail
        })?;
        debugln!("try to update ice cands {}", s);
        let buff_len = s.len() * 2;
        let mut tmp = Vec::with_capacity(buff_len);
        unsafe { tmp.set_len(buff_len) };
        let v = binascii::b64encode(s.as_bytes(), tmp.as_mut_slice()).map_err(|e| {
            errorln!(
                "base64 ice cands failed: {:?}, len {} base64 buffer len {}",
                e,
                s.len(),
                buff_len
            );
            signal_error_e_signal_error_invalid_param
        })?;
        let body = format!(
            r#"{{ "cmd":{},"data":{{"session_id":"{}","cands":"{}"}}}}"#,
            u8::from(RequestCmd::UpdateIceCands),
            ses.id,
            unsafe { alloc::str::from_utf8_unchecked(v) }
        );
        debugln!(
            "session {} update ice cands: {}",
            session_id,
            sdp.to_str().unwrap_or("invalid ice cands")
        );
        self.send(body.as_str())
    }

    pub fn handle_msg(&mut self, to_in_ms: u32) -> Result<Option<signal_msg_t>> {
        if self.framer.is_null() {
            errorln!("Bug: framer not inited");
            return Err(signal_error_e_signal_error_invalid_param);
        }
        let framer =
            unsafe { Box::leak(Box::from_raw(self.framer as *mut Framer<EmptyRng, Client>)) };
        if let Some(ref mut stream) = self.stream {
            let cur_time = now().as_secs();
            if cur_time > self.last_hb_time + HEARTBEAT_INTERVAL / 2 {
                framer
                    .write(stream, WebSocketSendMessageType::Ping, true, &[0])
                    .map_err(|e| {
                        errorln!("send ping message failed: {:?}", e);
                        signal_error_e_signal_error_disconnect
                    })?;
                self.last_hb_time = cur_time;
            }
            let _ = stream.set_recv_timeout(to_in_ms);
            let ret = match framer.read(stream, &mut self.frame_buf) {
                Ok(ReadResult::Text(s)) => {
                    let content = s.to_owned();
                    self.handle_text(content.as_str())
                }
                Ok(ReadResult::Closed) => {
                    //not data
                    Ok(None)
                }
                Ok(ReadResult::Pong(_)) => {
                    debugln!("pong");
                    Ok(None)
                }
                Ok(ReadResult::Binary(_)) => {
                    errorln!("binary not support");
                    Ok(None)
                }
                Err(e) => {
                    match e {
                        FramerError::Io(IoError::Timeout) => {}
                        e => {
                            errorln!("error {:?}", e);
                        }
                    }
                    Ok(None)
                }
            };
            return ret;
        }
        Ok(None)
    }

    fn handle_text(&mut self, txt: &str) -> Result<Option<signal_msg_t>> {
        debugln!("from signal server: {}", txt);
        let root = Json::parse(txt).map_err(|e| {
            errorln!("parse json [{}] failed: {:?}", txt, e);
            signal_error_e_signal_error_fail
        })?;
        let cv = root.get(cstr_ptr!("cmd")).ok_or_else(|| {
            errorln!("cmd not found");
            signal_error_e_signal_error_fail
        })?;
        let c = cv.as_u64().ok_or(signal_error_e_signal_error_fail)? as u8;
        debugln!("cmd {} v {}", cv.to_string(), c);
        let cmd = SessionNotifyCmd::from(c);
        let session_id = root.get(cstr_ptr!("session_id")).ok_or_else(|| {
            errorln!("session_id not found");
            signal_error_e_signal_error_fail
        })?;
        let session_id = session_id
            .as_str()
            .ok_or(signal_error_e_signal_error_fail)?;
        debugln!("notify cmd {:?}", cmd);
        match cmd {
            SessionNotifyCmd::Created => self.handle_cmd_session_created(&root, session_id),
            SessionNotifyCmd::AnswerAccepted => {
                self.handle_cmd_session_answer_accepted(&root, session_id)
            }
            SessionNotifyCmd::CreatedRequest => {
                self.handle_cmd_session_create_request(&root, session_id)
            }
            SessionNotifyCmd::StartP2p => self.handle_cmd_start_p2p(&root, session_id),
            SessionNotifyCmd::RequestToClose => self.handle_cmd_request_close(&root, session_id),
            SessionNotifyCmd::HasError => self.handle_cmd_session_has_error(&root, session_id),
            SessionNotifyCmd::StatusReport => self.handle_cmd_status_report(&root, session_id),
            SessionNotifyCmd::UpdateIceCands => self.handle_cmd_update_ice_cands(&root, session_id),
        }
    }

    fn find_session(&self, session_id: &str) -> Option<usize> {
        for i in 1..self.sessions.len() {
            if let Some(ref ses) = self.sessions[i] {
                if session_id == ses.id {
                    return Some(i);
                }
            }
        }
        None
    }

    fn get_session_id(&self, session_id: session_id_t) -> Option<Session> {
        if let Some(v) = self.sessions.get(session_id as usize) {
            if let Some(ses) = v {
                return Some(ses.clone());
            }
        }
        None
    }

    fn add_session(&mut self, session_id: &str) -> Option<usize> {
        for i in 1..self.sessions.len() {
            if self.sessions[i].is_none() {
                self.sessions[i] = Some(Session {
                    id: session_id.to_owned(),
                    ctime: now().as_secs(),
                    utime: now().as_secs(),
                });
                infoln!("add session, slot {} mapped to session {}", i, session_id);
                return Some(i);
            }
        }
        None
    }

    fn remote_session(&mut self, session_id: &str) -> Option<Session> {
        if let Some(index) = self.find_session(session_id) {
            if let Some(session) = self.sessions[index].take() {
                infoln!(
                    "remove session, slot {} mapped to session {}",
                    index,
                    session.id
                );
            }
        }
        None
    }

    fn handle_cmd_session_created(
        &mut self,
        root: &Json,
        session_id: &str,
    ) -> Result<Option<signal_msg_t>> {
        infoln!("session {} created", session_id);
        if let Some(index) = self.add_session(session_id) {
            //handle ice
            let ice = Self::parse_ice(root, session_id)?;
            let src = unsafe {
                core::slice::from_raw_parts(
                    &ice as *const ice_info_t as *const u8,
                    core::mem::size_of_val(&ice),
                )
            };
            Ok(Some(signal_msg_t {
                msg_id: signal_msg_id_t_smi_session_created,
                session_id: index as u8,
                data: buffer_t::from(Vec::from(src)),
                destroy: Some(destroy_signal_msg),
            }))
        } else {
            errorln!("add session {} failed", session_id);
            Ok(None)
        }
    }

    fn handle_cmd_request_close(
        &mut self,
        _root: &Json,
        session_id: &str,
    ) -> Result<Option<signal_msg_t>> {
        match self.find_session(session_id) {
            Some(index) => {
                self.remote_session(session_id);
                return Ok(Some(signal_msg_t {
                    msg_id: signal_msg_id_t_smi_session_closing,
                    session_id: index as u8,
                    data: buffer_t::from(Vec::new()),
                    destroy: Some(destroy_signal_msg),
                }));
            }
            None => {
                errorln!("session {} not found!", session_id);
            }
        }
        Ok(None)
    }

    fn handle_cmd_session_create_request(
        &mut self,
        root: &Json,
        session_id: &str,
    ) -> Result<Option<signal_msg_t>> {
        if let Some(index) = self.add_session(session_id) {
            //handle ice
            let info = session_create_by_remote_t {
                sdp: Self::parse_sdp(root, session_id)?,
                ice: Self::parse_ice(root, session_id)?,
            };
            let src = unsafe {
                core::slice::from_raw_parts(
                    &info as *const session_create_by_remote_t as *const u8,
                    core::mem::size_of_val(&info),
                )
            };
            return Ok(Some(signal_msg_t {
                msg_id: signal_msg_id_t_smi_session_created_by_remote,
                session_id: index as u8,
                data: buffer_t::from(Vec::from(src)),
                destroy: Some(destroy_signal_msg),
            }));
        } else {
            errorln!("session add failed, maybe full!");
            self.send_err_report(
                session_id,
                1,
                format!("session id {} create failed:session full!", session_id),
            );
        }
        Ok(None)
    }

    fn parse_ice(root: &Json, _session_id: &str) -> Result<ice_info_t> {
        let mut ice: ice_info_t = unsafe { core::mem::zeroed() };
        let data = root.get(cstr_ptr!("data")).ok_or_else(|| {
            errorln!("data field lost");
            signal_error_e_signal_error_fail
        })?;
        let ice_node = data.get(cstr_ptr!("ice")).ok_or_else(|| {
            errorln!("ice field lost");
            signal_error_e_signal_error_fail
        })?;
        let stun = ice_node.get(cstr_ptr!("stun")).ok_or_else(|| {
            errorln!("stun field lost");
            signal_error_e_signal_error_fail
        })?;
        json_get_str!(stun_addr, stun, "address");
        copy_str_to_slice(stun_addr, unsafe {
            core::mem::transmute(&mut ice.stun_server[..])
        });
        let turn = ice_node.get(cstr_ptr!("turn")).ok_or_else(|| {
            errorln!("turn field lost");
            signal_error_e_signal_error_fail
        })?;
        json_get_str!(turn_addr, turn, "address");
        json_get_str!(turn_user, turn, "user");
        json_get_str!(turn_pwd, turn, "pwd");
        copy_str_to_slice(turn_addr, unsafe {
            core::mem::transmute(&mut ice.turn_server[..])
        });
        copy_str_to_slice(turn_user, unsafe {
            core::mem::transmute(&mut ice.turn_user[..])
        });
        copy_str_to_slice(turn_pwd, unsafe {
            core::mem::transmute(&mut ice.turn_pwd[..])
        });
        Ok(ice)
    }

    fn handle_cmd_session_answer_accepted(
        &mut self,
        _root: &Json,
        session_id: &str,
    ) -> Result<Option<signal_msg_t>> {
        match self.find_session(session_id) {
            Some(index) => {
                return Ok(Some(signal_msg_t {
                    msg_id: signal_msg_id_t_smi_session_created_by_remote,
                    session_id: index as u8,
                    data: buffer_t::from(Vec::new()),
                    destroy: Some(destroy_signal_msg),
                }));
            }
            None => {
                errorln!("session {} not created!", session_id);
            }
        }
        Ok(None)
    }

    fn handle_cmd_start_p2p(
        &mut self,
        root: &Json,
        session_id: &str,
    ) -> Result<Option<signal_msg_t>> {
        match self.find_session(session_id) {
            Some(index) => {
                //handle sdp
                let sdp = Self::parse_sdp(root, session_id)?;
                let src = unsafe {
                    core::slice::from_raw_parts(
                        &sdp as *const session_sdp_info_t as *const u8,
                        core::mem::size_of_val(&sdp),
                    )
                };
                return Ok(Some(signal_msg_t {
                    msg_id: signal_msg_id_t_smi_remote_sdp_arrived,
                    session_id: index as u8,
                    data: buffer_t::from(Vec::from(src)),
                    destroy: Some(destroy_signal_msg),
                }));
            }
            None => {
                errorln!("session {} not found!", session_id);
            }
        }
        Ok(None)
    }

    fn parse_sdp(root: &Json, _session_id: &str) -> Result<session_sdp_info_t> {
        let mut sdp: session_sdp_info_t = unsafe { core::mem::zeroed() };
        let data = root.get(cstr_ptr!("data")).ok_or_else(|| {
            errorln!("data field lost");
            signal_error_e_signal_error_fail
        })?;
        json_get_str!(sdp_info, data, "sdp");
        let buff_len = sdp_info.len() * 2;
        let mut tmp = Vec::with_capacity(buff_len);
        unsafe {
            tmp.set_len(buff_len);
        }
        let decoded_sdp =
            binascii::b64decode(sdp_info.as_bytes(), tmp.as_mut_slice()).map_err(|e| {
                errorln!("sdp base64 decode failed:{:?}", e);
                signal_error_e_signal_error_fail
            })?;
        debugln!("sdp: {}", unsafe {
            core::str::from_utf8_unchecked(decoded_sdp)
        });
        sdp.sdp = buffer_t::from(Vec::from(decoded_sdp));
        Ok(sdp)
    }

    fn handle_cmd_session_has_error(
        &mut self,
        root: &Json,
        session_id: &str,
    ) -> Result<Option<signal_msg_t>> {
        let index = match self.find_session(session_id) {
            Some(index) => index,
            None => {
                errorln!("session {} not found!", session_id);
                INVALID_SESSION_ID
            }
        };
        let err_info = Self::parse_err(root, session_id)?;
        let s: &[u8] = unsafe { core::mem::transmute(&err_info.rd[..]) };
        let rd = unsafe { core::str::from_utf8_unchecked(s) };
        errorln!("session {} has error {} {}", session_id, err_info.rc, rd);
        let src = unsafe {
            core::slice::from_raw_parts(
                &err_info as *const session_error_info_t as *const u8,
                core::mem::size_of_val(&err_info),
            )
        };
        return Ok(Some(signal_msg_t {
            msg_id: signal_msg_id_t_smi_session_error,
            session_id: index as u8,
            data: buffer_t::from(Vec::from(src)),
            destroy: Some(destroy_signal_msg),
        }));
    }

    fn parse_err(root: &Json, _session_id: &str) -> Result<session_error_info_t> {
        let mut error: session_error_info_t = unsafe { core::mem::zeroed() };
        let data = root.get(cstr_ptr!("data")).ok_or_else(|| {
            errorln!("data field lost");
            signal_error_e_signal_error_fail
        })?;
        let error_node = data.get(cstr_ptr!("error")).ok_or_else(|| {
            errorln!("error field lost");
            signal_error_e_signal_error_fail
        })?;
        json_get_int!(rc, error_node, "rc");
        json_get_str!(rd, error_node, "rd");
        error.rc = rc as i32;
        copy_str_to_slice(rd, unsafe { core::mem::transmute(&mut error.rd[..]) });
        Ok(error)
    }

    fn handle_cmd_status_report(
        &mut self,
        root: &Json,
        session_id: &str,
    ) -> Result<Option<signal_msg_t>> {
        //report info
        match self.find_session(session_id) {
            Some(index) => {
                let status = Self::parse_status(root, session_id)?;
                let src = unsafe {
                    core::slice::from_raw_parts(
                        &status as *const session_status_info_t as *const u8,
                        core::mem::size_of_val(&status),
                    )
                };
                return Ok(Some(signal_msg_t {
                    msg_id: signal_msg_id_t_smi_session_status,
                    session_id: index as u8,
                    data: buffer_t::from(Vec::from(src)),
                    destroy: Some(destroy_signal_msg),
                }));
            }
            None => {
                errorln!("session {} not found!", session_id);
            }
        }
        Ok(None)
    }

    fn parse_status(root: &Json, _session_id: &str) -> Result<session_status_info_t> {
        let mut status: session_status_info_t = unsafe { core::mem::zeroed() };
        let data = root.get(cstr_ptr!("data")).ok_or_else(|| {
            errorln!("data field lost");
            signal_error_e_signal_error_fail
        })?;
        json_get_int!(status_v, data, "status");
        status.status = status_v as u32;
        Ok(status)
    }

    fn handle_cmd_update_ice_cands(
        &mut self,
        root: &Json,
        session_id: &str,
    ) -> Result<Option<signal_msg_t>> {
        match self.find_session(session_id) {
            Some(index) => {
                //handle sdp
                let sdp = Self::parse_cands(root, session_id)?;
                let src = unsafe {
                    core::slice::from_raw_parts(
                        &sdp as *const session_ice_cands_update_t as *const u8,
                        core::mem::size_of_val(&sdp),
                    )
                };
                return Ok(Some(signal_msg_t {
                    msg_id: signal_msg_id_t_smi_remote_ice_candidate_update,
                    session_id: index as u8,
                    data: buffer_t::from(Vec::from(src)),
                    destroy: Some(destroy_signal_msg),
                }));
            }
            None => {
                errorln!("got remote ice cands: session {} not found!", session_id);
            }
        }
        Ok(None)
    }

    fn parse_cands(root: &Json, _session_id: &str) -> Result<session_ice_cands_update_t> {
        let mut info: session_ice_cands_update_t = unsafe { core::mem::zeroed() };
        let data = root.get(cstr_ptr!("data")).ok_or_else(|| {
            errorln!("data field lost");
            signal_error_e_signal_error_fail
        })?;
        json_get_str!(sdp_info, data, "cands");
        let buff_len = sdp_info.len() * 2;
        let mut tmp = Vec::with_capacity(buff_len);
        unsafe {
            tmp.set_len(buff_len);
        }
        let decoded_sdp =
            binascii::b64decode(sdp_info.as_bytes(), tmp.as_mut_slice()).map_err(|e| {
                errorln!("ice cands base64 decode failed:{:?}", e);
                signal_error_e_signal_error_fail
            })?;
        debugln!("ice cands: {}", unsafe {
            core::str::from_utf8_unchecked(decoded_sdp)
        });
        info.cands = buffer_t::from(Vec::from(decoded_sdp));
        Ok(info)
    }

    fn get_session_index(&self, session_id: &str) -> Result<usize> {
        self.find_session(session_id).ok_or_else(|| {
            errorln!("session {} not found!", session_id);
            signal_error_e_signal_error_fail
        })
    }
}

impl Drop for WsClient {
    fn drop(&mut self) {
        unsafe {
            if !self.framer.is_null() {
                let p = Box::from_raw(self.framer as *mut Framer<EmptyRng, Client>);
                drop(p);
            }
        }
        debugln!("drop wsclient");
        self.close();
    }
}
